#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

#include <map>
#include <string>

#include "mqttclient_communication.h"
#include "device_communication.h"
 

// 存放所有在线设备的信息（设备ID、连接套接字等）
static std::map<std::string, int> online_devs;

 
static int recv_line(int sock, char* buff, int size);
static void* do_device_comm(void* arg);


void device_comm(void)
{
    // 第 1 步:创建监听套接字
    int sock_listen = socket(AF_INET, SOCK_STREAM, 0);

    if(sock_listen == -1)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置套接字的地址复用属性为 1，以允许地址复用，从而实现快速重启服务器
    int opt_val = 1;
    setsockopt(sock_listen, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof(opt_val));
    
    // 第 2 步:绑定地址
    struct sockaddr_in myaddr;
    myaddr.sin_family = AF_INET;          // 指定地址家族为 Internet 地址家族(IPv4)
    myaddr.sin_addr.s_addr = INADDR_ANY;  // 指定 IP 地址为本机任意 IP 地址
    //myaddr.sin_addr.s_addr = inet_addr("192.168.0.36");  // 指定 IP 地址为本机某个有效的 IP 地址
    myaddr.sin_port = htons(9413);        // 指定端口号为 9413

    if(-1 == bind(sock_listen, (struct sockaddr*)&myaddr, sizeof(myaddr)))
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    // 第 3 步:监听
    if(-1 == listen(sock_listen, 5))
    {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    int sock_conn;
    struct sockaddr_in cliaddr;
    socklen_t addrlen = sizeof(cliaddr);
    struct timeval t;
    t.tv_sec = 10;
    t.tv_usec = 0;

    while(1)
    {
        // 第 4 步:接收客户端连接请求
        //int sock_conn = accept(sock_listen, NULL, NULL);  // 如果对客户端地址不感兴趣
        sock_conn = accept(sock_listen, (struct sockaddr*)&cliaddr, &addrlen);  // 如果对客户端地址感兴趣

        if(sock_conn == -1)
        {
            perror("accept");
            continue;
        }

        // 创建一个新线程
        pthread_t tid;
        if(pthread_create(&tid, NULL, do_device_comm, (void*)(long)sock_conn))
        {
            perror("pthread_create");
            close(sock_conn);
            continue;
        }

	    setsockopt(sock_conn, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));

        // 将线程设置为分离状态(detach state)，以保证线程结束后其占用的资源会被系统自动释放
        pthread_detach(tid);

        printf("客户端(%s:%d)已上线！\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
    }

    // 第 7 步:关闭监听套接字
    close(sock_listen);
}

 
// 定义线程函数
static void* do_device_comm(void* arg)
{
	int sock_conn = (long)arg;

        // 第 5 步:收发数据
	char data[500];
	int ret, len;
	char* p = NULL;
	const char* topic = NULL, *payload = NULL;
	char dev_id[33];  // 设备ID最长32个字符，最短3个字符！

	while((ret = recv_line(sock_conn, data, sizeof(data))) > 0)
	{
		printf("客户端说：%s\n", data);

		p = strchr(data, ' ');

		if(p == NULL) break;

		*p = '\0';

		topic = data;
		payload = p + 1;

		if(*payload == '\0') break;

		p = (char*)strchr(topic, '/');

		if(p == NULL) break;

		// 校验设备ID的长度是否合法
		len = p - topic;

		if(len < 3 || len > 32) break;

		strncpy(dev_id, topic, len);
		dev_id[len] = '\0';

		online_devs[dev_id] = sock_conn;

		mqttclient_pub_msg(topic, payload);
	}

    // 第 6 步:断开连接
    close(sock_conn);

	return NULL;
}


int device_send_cmd(const char* dev_id, const void* cmd, int size)
{
    auto it = online_devs.find(dev_id);

    if(it == online_devs.end()) return 1;

    // 如果目标设备在线，就转发控制指令
    if(send(online_devs[dev_id], cmd, size, 0) > 0)
        return 0;

    return 2;
}


// 接收一行数据
static int recv_line(int sock, char* buff, int size)
{
	char c = 0;
	int cnt = 0, ret;

	while(cnt < size - 1)
	{
		ret = recv(sock, &c, 1, 0);

		if(ret < 0) return -1;

		if(ret == 0 || c == '\n') break;

		buff[cnt] = c;

		cnt++;
	}

	if(c != '\n') return -1;

	buff[cnt] = '\0';

	return cnt;
}

